#include <iostream>
using namespace std;


// polymorhpism can happen only when the type are cast compatible
struct T {
    virtual const T& add(const T& t2, T& t)=0;
	virtual void print() const =0;
};

struct T1 : public T {
    float val;
    T1 (float v) : val(v) {}
    const T1& add(const T& t2, T& t){
        const T1& other = (const T1&) t2;
        T1& res = (T1&) t;
        cout << "T1 +" << endl;
        res.val = (val + other.val);
        return (const T1&) t;
    }

	void print() const {
		cout << val << endl;
	}
};

struct T2 : public T {
    int val;
    T2 (int v) : val(v) {}
   	const T& add(const T& t2, T& t){
        const T2& other = (const T2&)t2;
        T2& res = (T2&)t;
        cout << "T2 +" << endl;
        res.val = (val + other.val);
        return t;
    }
	
	void print() const{
		cout << val << endl;
	}
 
};

int main()
{
    T1 a(1.1);
    T1 b(2.2);
    T2 c(3);
    T2 d(4);

	T2 e=c.add(d,c);
	e.print();
	c.print();
	
	cout << "a:";
	a.print();
	cout << "b:";
	b.print();
	cout << "c:";
	c.print();
	cout << "d:";
	d.print();

    T *pa;
	pa= &a; 


	cout <<"a=a+b by pointer: \t";
	(pa->add(b,a)).print(); 

	cout <<"a=a+b by reference: \t";
	(a.add(b,a)).print(); 


	cout << "a:";
	a.print();

	pa= &c; 

	cout <<"c=c+d by pointer: \t";
	(pa->add(d,c)).print(); 

	cout << "c:";
	c.print();

	T& ra=a;
	cout <<"a=a+b by reference: \t";
	(ra.add(b,a)).print(); 

	cout << "a:";
	ra.print();

	
	T& rc=c;
	cout <<"c=c+d by reference: \t";
	(rc.add(d,c)).print(); 
	

	cout << "c:";
	rc.print();


    return 0;
}
